/*
 * dispatcher.hpp
 *
 *  Created on: Feb 10, 2011
 *      Author: daniel
 */

#ifndef DISPATCHER_HPP_
#define DISPATCHER_HPP_

#include <map>
#include <string>
#include <list>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/unordered_map.hpp>
#include <doeplus/webapp/controller.hpp>

namespace doeplus{ namespace webapp{

enum dispatch_options{
	option_threaded	= 1
};

class controller;


class dispatcher_base{
public:
	typedef boost::shared_ptr<dispatcher_base> ptr;
	virtual controller *create()=0;
	unsigned int options;

protected:
	typedef std::map<std::string,dispatcher_base::ptr> controllers_map;
	controllers_map controllermap;
public:
	template<typename C> void add_controller(const char* path,unsigned int options=0);
	virtual bool dispatch(std::list<controller::ptr> &controllers,std::list<std::string> &paths,doeplus::webapp::connection::ptr connection) = 0;

};

template<typename T> class dispatcher : public dispatcher_base{

public:
	controller *create(){
		return new T();
	}

	typedef boost::function2<void,T*,doeplus::webapp::connection::ptr> action_handler;
//protected:
	typedef std::map<std::string,action_handler> actions_map;
	actions_map actionmap;

	void add_action( action_handler func,const char* path){
		actionmap[path]	= func;
	}

	bool dispatch(std::list<controller::ptr> &controllers,std::list<std::string> &paths,doeplus::webapp::connection::ptr connection){
	    if(paths.size()){
            std::string next_path	= paths.front();

            {
                typename controllers_map::iterator it = controllermap.find(next_path);
                if(it!=controllermap.end()){
                    paths.pop_front();
                    controller::ptr ctrl(it->second->create());
                    controllers.push_back(ctrl);
                    return it->second->dispatch(controllers,paths,connection);
                }
            }


            // No controller found, lets see if there is an action
            {
                typename actions_map::iterator ait = actionmap.find(next_path);
                if(ait!=actionmap.end()){
                    paths.pop_front();
                    // Execute the action
                    (ait->second)((T*)controllers.back().get(),connection);
                    return true;
                }
            }
	    }
        return false;
	}

};


template<typename C> void dispatcher_base::add_controller(const char* path,unsigned int options){
	dispatcher<C> *holder	= new dispatcher<C>();
	holder->options	= options;
	dispatcher_base::ptr disp_ptr(holder);
	controllermap[path]	= disp_ptr;
	C::dispatch_setup(holder);
}


} }

#endif /* DISPATCHER_HPP_ */
